﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Threading.Tasks;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;

namespace SNSComponent.SNSProvider
{
    public class DoubanProvider : BaseOAuth2Provider
    {

        protected string AccessToken { get; private set; }

        protected string RefreshToken { get; private set; }

        public DateTime ExpressDate { get; private set; }

        public DateTime AutheDate { get; private set; }

        public string UserID { get; private set; }

        public string UserName { get; private set; }

        public DoubanProvider(string appKey, string appSecret, Uri redirectUri,string providerName)
            :base(appKey,appSecret,redirectUri,providerName)
        {
        }

        protected override Uri GetAuthUri()
        {
            return new Uri(string.Format("https://www.douban.com/service/auth2/auth?client_id={0}&redirect_uri={1}&response_type=code&scope=shuo_basic_r,shuo_basic_w,douban_basic_common",
                System.Net.HttpUtility.UrlEncode(AppKey), 
                System.Net.HttpUtility.UrlEncode(RedirectUri.ToString())));
        }

        string _veriferCode;
        protected override void OnUserAuthing(object sender, UserAuthEventArgs e)
        {
            if (e.VerifierUri.Query.ToLower().Contains("code="))
            {
                string[] tmp = e.VerifierUri.Query.Split('=');
                if (tmp.Length >= 2)
                {
                    _veriferCode = tmp[1];
                    e.IsAuth = true;
                }

            }
        }

        protected override async  Task<ResultData> GetAccessToken(Uri verifyUri)
        {
            Uri postUri = new Uri(string.Format("https://www.douban.com/service/auth2/token?client_id={0}&client_secret={1}&redirect_uri={2}&grant_type=authorization_code&code={3}",
                System.Net.HttpUtility.UrlEncode(AppKey),
                System.Net.HttpUtility.UrlEncode(AppSecret),
                System.Net.HttpUtility.UrlEncode(RedirectUri.ToString()),
                System.Net.HttpUtility.UrlEncode(_veriferCode)));

            Dictionary<string, string> header = new Dictionary<string, string>();
            header.Add("Content-Type", "application/x-www-form-urlencoded");
            try
            {
                return await Utiltiy.HttpUtility.Post(postUri, null, header, (webResponse) =>
                    {
                        using (System.IO.Stream stream = webResponse.GetResponseStream())
                        {
                            System.Runtime.Serialization.Json.DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(DoubanAuthInfor));

                            DoubanAuthInfor authInfor = serializer.ReadObject(stream) as DoubanAuthInfor;
                            if (authInfor != null)
                            {

                                this.AccessToken = authInfor.AccessToken;
                                this.RefreshToken = authInfor.RefreshToken;
                                this.AutheDate = DateTime.Now;
                                this.ExpressDate = AutheDate.AddSeconds(authInfor.ExpressionIn);
                                this.UserID = authInfor.UserID;
                                this.UserName = authInfor.UserName;
                                IsAuthed = true;
                            }
                            ResultData result = new ResultData(this,ResultState.Success);

                            return result;
                        }

                    });
            }
            catch (WebException webEx)
            {
                return  WebExcptionHandler(webEx);
            }
            catch (Exception ex)
            {
                return new ResultData(this,ResultState.UnknowError, ex);
            }
        }

        #region Share Status

        public override async Task<ResultData> ShareStatus(string content)
        {
            return await PostShuoStatus(content);
        }

        public override async Task<ResultData> ShareStatus(string context, System.IO.Stream image)
        {
            throw new NotImplementedException();
        }

        public override Task<ResultData> ShareStatusWithLocation(string conetxt)
        {
            throw new NotImplementedException();
        }

        public override Task<ResultData> ShareStatusWithLocation(string context, Stream image)
        {
 	        throw new NotImplementedException();
        }

        public override Task<ResultData> ShareStatusWithLocation(string context, Uri imageUri)
        {
            throw new NotImplementedException();
        }

        public override Task<ResultData> ShareStatus(string context, Uri imageUri)
        {
            throw new NotImplementedException();
        }

        private async Task<ResultData> PostShuoStatus(string statusContent)
        {
            Dictionary<string, string> header = new Dictionary<string, string>();
            header.Add("Authorization", "Bearer " + AccessToken);

            try
            {
                return await Utiltiy.HttpUtility.Get(new Uri("https://api.douban.com/shuo/v2/statuses?source=" + AppKey + "&text1=" + HttpUtility.UrlEncode(statusContent)), header, (webResponse) =>
                {
                    return new ResultData(this, ResultState.Success);
                });
            }
            catch (WebException webEx)
            {
                return WebExcptionHandler(webEx);
            }
            catch (Exception ex)
            {
                return new ResultData(this, ResultState.UnknowError, ex);
            }
        }

        #endregion


        protected override System.IO.Stream ToStream()
        {
            System.Runtime.Serialization.Json.DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(DoubanAuthInfor));
            System.IO.MemoryStream stream = new System.IO.MemoryStream();
            serializer.WriteObject(stream, new DoubanAuthInfor() {
                UserName = this.UserName,
                UserID = this.UserID, 
                AccessToken = this.AccessToken, 
                RefreshToken = this.RefreshToken, 
                AuthDate = this.AutheDate,
                ExpressDate = this.ExpressDate });

            stream.Seek(0, System.IO.SeekOrigin.Begin);
            return stream;
        }

        protected override void LoadFromeStrem(System.IO.Stream localData)
        {
            if (localData == null) return;
            System.Runtime.Serialization.Json.DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(DoubanAuthInfor));
            System.IO.MemoryStream stream = new System.IO.MemoryStream();
            DoubanAuthInfor authInfor = serializer.ReadObject(localData) as DoubanAuthInfor;
            if (authInfor == null) return;
            this.AccessToken = authInfor.AccessToken;
            this.RefreshToken = authInfor.RefreshToken;
            this.ExpressDate = authInfor.ExpressDate;
            this.UserID = authInfor.UserID;
            this.UserName = authInfor.UserName;
            this.AutheDate = authInfor.AuthDate;
            IsAuthed = true;
           
            
        }

        private ResultData WebExcptionHandler(WebException webEx)
        {
            HttpWebResponse response = webEx.Response as HttpWebResponse;
            if (response == null) return new ResultData(this,ResultState.UnknowError);

            Dictionary<string, object> data = new Dictionary<string, object>();
            ResultState state = ResultState.UnknowError;
            try
            {
                if (response.StatusCode == HttpStatusCode.BadRequest)
                {
                    using (System.IO.Stream stream = response.GetResponseStream())
                    {
                        System.Runtime.Serialization.Json.DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(DoubanErrorMessage));

                        DoubanErrorMessage msg = serializer.ReadObject(response.GetResponseStream()) as DoubanErrorMessage;

                        data.Add("Code", msg.Code);
                        data.Add("Message", msg.Message);
                        data.Add("Request", msg.Request);

                        switch (msg.Code)
                        {
                            case "102":
                                state = ResultState.InvalidToken;
                                break;
                            case "103":
                                state = ResultState.InvalidToken;
                                break;
                            case "104":
                                state = ResultState.InvalidAPIKey;
                                break;
                            case "105":
                                state = ResultState.InvalidAPIKey;
                                break;
                            case "106":
                                state = ResultState.InvalidToken;
                                break;
                            case "116":
                                state = ResultState.InvalidAPKSecet;
                                break;
                            case "117":
                                state = ResultState.RedirectUriMismatch;
                                break;
                            case "118":
                                state = ResultState.InvalidVerifyCode;
                                break;
                            case "119":
                                state = ResultState.InvalidToken;
                                break;
                            case "123":
                                state = ResultState.InvalidToken;
                                break;
                            case "124":
                                state = ResultState.InvalidToken;
                                break;
                            default:
                                state = ResultState.RequestForbidden;
                                break;
                        }
                    }
                   
                }
            }
            catch
            { }


            return new ResultData(this,state, webEx, data);
        }


        [DataContract]
        private class  DoubanAuthInfor
        {
            [DataMember(Name = "access_token")]
            public string AccessToken { get; set; }

            [DataMember(Name = "expires_in")]
            public long ExpressionIn { get; set; }

            [DataMember(Name = "refresh_token")]
            public string RefreshToken { get; set; }

            [DataMember(Name = "douban_user_id")]
            public string UserID { get; set; }

            [DataMember(Name="douban_user_name")]
            public string UserName { get; set; }

            [DataMember(Name="Auth_Date")]
            public DateTime AuthDate { get; set; }

            [DataMember(Name="Express_Date")]
            public DateTime ExpressDate { get; set; }



        }

        [DataContract]
        private class DoubanErrorMessage
        {
            [DataMember(Name="msg")]
            public string Message { get; set; }

            [DataMember(Name="code")]
            public string Code { get; set; }

            [DataMember(Name="request")]
            public string Request { get; set; }
            
        }
    }


}
